package eva.savant.business.material.service

import eva.savant.core.service.TSEntity
import eva.savant.businessobject.mt.Motivo
import eva.savant.businessobject.mt.Kardex
import eva.savant.businessobject.mt.Almacen
import eva.savant.businessobject.mt.Material
import eva.savant.businessobject.mt.Movimiento
import eva.savant.businessobject.mt.Inventario
import org.springframework.stereotype.Repository

@Repository(value = 'tsKardex')
class TSKardexImpl extends TSEntity implements TSKardex{

  //Para realizar la valuacion se usa la tabla inventario

  public Movimiento saveInKardex(Movimiento entity) throws Exception { sociedadId = entity.sociedadId

    if(entity.fecha == null) throw new Exception ('La fecha del movimiento de inventario es invalido')

    if(entity.lsKardex.size() < 1) throw new Exception ('El detalle productos esta vacio')

    if(entity.tipo.equals(EGRESO)) validarStockDisponible(entity.lsKardex)


    if(entity.tipo.equals(EGRESO)) {

    def lsKardex= new ArrayList<Kardex>() //valuate kardex

    for(k in entity.lsKardex) lsKardex.addAll(valuateOut(k));   entity.lsKardex= lsKardex }


    for(x in entity.lsKardex) {

    def i= get(Inventario.class,'almacen.id=? and material.id=?',x.almacen.id,x.material.id)


      switch (entity.tipo)  {

      case INGRESO:

      if(i == null) i= new Inventario(get(Almacen.class,  x.almacen.id ),

                                      get(Material.class, x.material.id),

                                      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

                                      entity.fecha, x.tipo, null,null,null)

      i.actual            = i.actual + x.entradaCantidad

      i.entradaCantidad   = x.entradaCantidad

      i.entradaCosto      = x.entradaCosto

      i.entradaValor      = i.entradaCantidad * i.entradaCosto

      i.saldoCantidad     = i.saldoCantidad + i.entradaCantidad

      i.saldoValor        = i.saldoValor + i.entradaValor

      i.saldoCosto        = i.material.valuacion == PROMEDIO && i.saldoCantidad!=0? (i.saldoValor / i.saldoCantidad) : i.entradaCosto

      x.entradaValor      = i.entradaValor

      x.valuacionCantidad = x.entradaCantidad

      break

      case EGRESO:

      i.actual            = i.actual - x.salidaCantidad

      i.salidaCantidad    = x.salidaCantidad

      i.salidaCosto       = x.salidaCosto

      i.salidaValor       = i.salidaCantidad * i.salidaCosto

      i.saldoCantidad     = i.saldoCantidad - i.salidaCantidad

      i.saldoValor        = i.saldoValor - i.salidaValor

      i.saldoCosto        = i.salidaCosto

      x.salidaValor       = i.salidaValor

      x.valuacionCantidad = x.salidaCantidad

      break  }


      i.fecha      = entity.fecha

      i.tipo       = entity.tipo

      i.operacionId= entity.operacionId

      i.sociedadId = entity.sociedadId

      i.usuarioId  = entity.usuarioId


      i= merge(i)


      x.movimiento      = entity

      x.saldoCantidad   = i.saldoCantidad

      x.saldoCosto      = i.saldoCosto

      x.saldoValor      = i.saldoValor

      x.fecha           = i.fecha

      x.tipo            = i.tipo

      x.valuacionMetodo = i.material.valuacion

      x.operacionId     = i.operacionId

      x.sociedadId      = i.sociedadId

      x.usuarioId       = i.usuarioId
    }

    return merge(entity)
  }

  private List<Kardex> valuateOut(Kardex x) throws Exception {

    //Dado material y cantidad se crea una lista de cantidades y costos de salida

    def order= x.material.valuacion == UEPS? 'desc' : 'asc'


    def lsKardex= x.material.valuacion == PROMEDIO ?

                  find(Kardex.class,'id=?',get(Kardex.class,"max(id): almacen.id=? and material.id=? and tipo='INGRESO'", x.almacen.id, x.material.id)) :

                  find(Kardex.class,"almacen.id=? and material.id=? and tipo='INGRESO' and valuacionCantidad > 0 order by id " + order, x.almacen.id, x.material.id)


    def lsDetail= new ArrayList<Kardex>()

    for(k in lsKardex) {

      if(x.salidaCantidad == 0) break

      def d= new Kardex()

      d.almacen=  x.almacen

      d.material= x.material

      d.persona=  x.persona

      d.salidaCosto= k.saldoCosto


      if(x.material.valuacion == PROMEDIO) { //descontamos cantidad segun metodo

                         d.salidaCantidad= x.salidaCantidad;    x.salidaCantidad= 0

      } else {           def saldo= k.valuacionCantidad - x.salidaCantidad

        if(saldo >= 0) { d.salidaCantidad= x.salidaCantidad;    x.salidaCantidad= 0;                                      k.valuacionCantidad= saldo

        }     else     { d.salidaCantidad= k.valuacionCantidad; x.salidaCantidad= x.salidaCantidad - k.valuacionCantidad; k.valuacionCantidad= 0     }

        merge(k)
      }


      lsDetail.add(d)
    }

    return lsDetail
  }


  public void validarStockDisponible(Collection<Kardex> lsKardex) throws Exception {

    def msg= ''


    for(x in lsKardex) {

    def i= get(Inventario.class,'almacen.id=? and material.id=?', x.almacen.id, x.material.id)

    if(i == null) { msg= msg + "[Alm $x.almacen.codigo no tiene el mat $x.material.codigo]\n" }

    if(i != null && i.actual < x.salidaCantidad && i.almacen.stock == CONT_STOCK)

    { msg= msg + "[Alm $x.almacen.codigo, mat $x.material.codigo, sol $x.salidaCantidad > disp $i.actual]\n" } }


    if(msg.length() > 0) throw new Exception (msg)
  }


  public boolean anularMovimiento(Integer id) throws Exception { //Anulacion = es como no realizar el movimiento de productos

    def e= get(Movimiento.class, id); if(e == null) return false

    e.motivo= get(Motivo.class,ANULADO)


    def m= new Movimiento()

    m.almacenero= e.almacenero

    m.motivo= e.motivo

    m.fecha= e.fecha

    m.documento= e.documento

    m.descripcion= 'Se anulo el movimiento ' + e.id

    m.tipo= e.tipo.equals(INGRESO)? EGRESO : INGRESO

    m.operacionId= ALTA

    m.sociedadId= e.sociedadId

    m.usuarioId= e.usuarioId

    m.lsKardex= new ArrayList<Kardex>()


    for(x in e.lsKardex) {

    def k= new Kardex()

    k.almacen        = x.almacen

    k.material       = x.material

    k.persona        = x.persona

    k.entradaCantidad= m.tipo.equals(INGRESO)? x.salidaCantidad  : null

    k.entradaCosto   = m.tipo.equals(INGRESO)? x.salidaCosto     : null

    k.entradaValor   = m.tipo.equals(INGRESO)? k.entradaCantidad * k.entradaCosto : null

    k.salidaCantidad = m.tipo.equals( EGRESO)? x.entradaCantidad : null

    m.lsKardex.add k }


    saveInKardex(m)

    return merge(e) != null
  }

}
